#include "CVideoTest.h"
#include "CAppCtx.h"
#include "CMediaFile.h"
#include "CTestReport.h"
#include "GenOpt.h"
#include "Logger.h"
#include "QsLog.h"
#include "radar_detect.h"
#include <QDateTime>
#include <QDir>
#include <qcontainerfwd.h>
#include <qdatetime.h>
#include <qprocess.h>
#include <qsurfaceformat.h>

int CThreadVideoTest::OnSocTrig(int soc_state, void *param) {
  CThreadVideoTest *pThis = (CThreadVideoTest *)param;
  if (soc_state == 0) {
    // SOC开机
    pThis->soc_on_ = true;
    pThis->soc_on_count_ = 0;
  } else if (soc_state == 1) {
    // SOC关机
    pThis->soc_on_ = false;
    pThis->soc_on_count_ = 0;
  } else {
    // SOC升级
  }
  return 0;
}

int CThreadVideoTest::OnPowerOn(QLogger &logger) {
  // 启动恢复到空闲状态
  last_alg_resl_ = 8;
  alg_wait_count_ = 5;
  return 0;
}

int CThreadVideoTest::OnRadarTrig(uint16_t *dist, uint16_t *amp, void *param) {
  CThreadVideoTest *pThis = (CThreadVideoTest *)param;
  uint32_t data = 0;
  if (pThis->radar_index_ > (pThis->radar_data_.size() - 1)) {
    data = pThis->radar_data_.at(pThis->radar_data_.size() - 1);
  } else {
    data = pThis->radar_data_.at(pThis->radar_index_);
  }

  *amp = data & 0xFFFF;
  *dist = (data >> 16) & 0xFFFF;
  pThis->radar_index_++;
  return 0;
}

uint64_t CThreadVideoTest::GetRadarInterval(QString video_file,
                                            uint32_t radar_num) {
  AVMMediaInfo_t info = {0};
  CMediaFile *file_vi = new CMediaFile();
  char *ch;
  QByteArray ba = video_file.toUtf8();
  ch = ba.data();
  int ret = file_vi->Open(ch, 0);
  if (ret != 0) {
    return ret;
  }
  file_vi->GetInfo(&info);
  file_vi->Close();
  delete file_vi;
  double video_in_sec = (double)info.total_frames / 25.0;
  total_frames_ = info.total_frames;
  uint64_t radar_interval_in_ms = (uint64_t)(1000 * video_in_sec / radar_num);
  return radar_interval_in_ms;
}

void CThreadVideoTest::ReadRadarDataFromFile(QString video_file,
                                             QVector<uint32_t> &radar_data) {
  QString file_radar = CGenOpt::GetRadarFileName(video_file);
  SEI_MAP raw_data;
  int ret = 0;
  ret = CMediaFile::ReadRadarDataFromFile(file_radar.toUtf8().constData(),
                                          raw_data);
  if (ret != 0) {
    return;
  }
  auto it = raw_data.begin();
  radar_data.clear();
  for (; it != raw_data.end(); it++) {
    uint8_t *raw_array = (uint8_t *)it->second.data();
    uint32_t dist = (uint32_t)raw_array[4] << 8 | raw_array[3];
    uint32_t amp = (uint32_t)(raw_array[6]) << 8 | raw_array[5];
    uint32_t data = (dist << 16) + amp;
    radar_data.append(data);
  }
}

void CThreadVideoTest::MakeAlgOutputFolder(QString file_name) {
  QDir dir;
  QString name = CGenOpt::GetFileNameFromPath(file_name);
  QFileInfo fileInfo(file_name);
  QString resl_output_path = fileInfo.absolutePath() + "/" + name;
  if (!dir.exists(resl_output_path)) {
    dir.mkpath(resl_output_path);
  }
}

void CThreadVideoTest::GenerateAlgParam(QString file_name, QStringList &args) {
  QString dev_ip = CAppCtx::instance()->GetDevIP();
  QString model_path = CAppCtx::instance()->GetModelPath();
  QString trig_type = QString::number(soc_trig_info_.trig_type);
  QString radar_dist = QString::number(soc_trig_info_.radar_data);
  QString radar_amp = QString::number(100);
  uint32_t frame_start = radar_index_ * total_frames_ / radar_data_.size();
  QString start_frame = QString::number(frame_start);
  QString park_state = QString::number(soc_trig_info_.park_state & 0x0F);
  QString radar_state;
  uint8_t radar_adj = ((soc_trig_info_.park_state & 0xF0) >> 4);
  if ((radar_adj == 1) || (radar_adj == 4)) {
    radar_state = "0";
  } else if (radar_adj == 2) {
    radar_state = "2";
  } else {
    radar_state = "1";
  }
  args.clear();
  args << dev_ip << model_path << file_name << trig_type << radar_dist
       << radar_amp << radar_state << start_frame << park_state;
}

void CThreadVideoTest::OnAlgResultProcess(QLogger &logger, int ret_code) {
  uint32_t frame_start = radar_index_ * total_frames_ / radar_data_.size();
  uint16_t frame_end = ret_code & 0x0000FFFF;
  last_alg_resl_ = (ret_code & 0xFFFF0000) >> 16;
  if (frame_end < frame_start) {
    alg_wait_count_ = 0;
  } else {
    alg_wait_count_ =
        (frame_end - frame_start) * radar_data_.size() / total_frames_;
  }
  logger.log(QString("算法返回状态: %1, 结束帧号: %2")
                 .arg(last_alg_resl_)
                 .arg(frame_end));
}

void CThreadVideoTest::StartAlgProc(QString file_name, QLogger &logger) {
  QStringList args;
  GenerateAlgParam(file_name, args);
  MakeAlgOutputFolder(file_name);
  logger.log(QString("雷达数据状态%2").arg(args.at(6)));
  QString command = CGenOpt::GetExePath() + "/" + "HLP_RTOS_test_stream.exe";
  QProcess *proc = new QProcess();
  logger.log(
      QString("算法执行命令: %1, 参数: %2").arg(command).arg(args.join(" ")));
  proc->start(command, args);
  proc->waitForFinished();
  int ret_code = proc->exitCode();
  OnAlgResultProcess(logger, ret_code);
}

int CThreadVideoTest::AddAlgResult(uint32_t id, uint8_t type) {
  CTestReport::EventInfo_t info = {0};
  info.id = id;
  // info.plate_num = mark_info_.plate_num;
  info.type = type;
  alg_resl_.append(info);
  return 0;
}

int CThreadVideoTest::OnAlgResultProcess(QLogger &logger, QString cur_file) {
  if (soc_on_) {
    if (soc_on_count_ == 0) {
      RadarDet_GetTrigInfo(&soc_trig_info_);
      logger.log(QString("SOC触发(雷达ID: %1): 原因: %2, 当前停车状态: %3")
                     .arg(radar_index_)
                     .arg(soc_trig_info_.trig_type)
                     .arg(soc_trig_info_.park_state & 0x0F));
      trig_index_ = radar_index_;
      if ((soc_trig_info_.trig_type & 2) != 0) {
        // 第一次上电,直接关机,配置算法模式空闲
        OnPowerOn(logger);
      } else if ((soc_trig_info_.trig_type & 1) != 0) {
        // 停车事件，启动算法计算结果
        StartAlgProc(cur_file, logger);
      } else {
        // 其他时间，使用上次的算法结果迅速关机
        last_alg_resl_ = soc_trig_info_.park_state & 0x0F;
        alg_wait_count_ = 5;
      }
    } else if (alg_wait_count_ == soc_on_count_) {
      /*处理延时结束，则配置算法模式关机*/
      if (last_alg_resl_ == 2) {
        AddAlgResult(trig_index_ - alg_wait_count_, 0);
      } else if (last_alg_resl_ == 4) {
        AddAlgResult(trig_index_ - alg_wait_count_, 1);
      }
      RadarDet_SetAlgResult(last_alg_resl_, 1);
      RadarDet_ShutSocPower();
      logger.log(QString("返回算法结果: 停车状态: %1").arg(last_alg_resl_));
      logger.log(QString("关机"));
      alg_wait_count_ = 0;
    }
    soc_on_count_++;
  }
  return 0;
}

int CThreadVideoTest::UpdateRadarData(QString file_name) {
  /*准备雷达数据*/
  radar_data_.clear();
  radar_index_ = 0;
  ReadRadarDataFromFile(file_name, radar_data_);
  return radar_data_.size();
}

int CThreadVideoTest::DoBeforeTest(QString file_name, CTestReport *report,
                                   QLogger &logger) {
  /*准备雷达数据*/
  if (UpdateRadarData(file_name) == 0) {
    QLOG_WARN() << QString("雷达数据为空，跳过该文件!");
  }
  logger.log(QString("测试文件: %1, 共%2个雷达数据")
                 .arg(file_name)
                 .arg(radar_data_.size()));

  /*计算雷达数据时间间隔*/
  radar_interval_ = GetRadarInterval(file_name, radar_data_.size());
  if (radar_interval_ == 0) {
    QLOG_WARN() << QString("视频文件异常，跳过该文件!");
    return -1;
  }
  RadarDet_SetExecInterval(radar_interval_);
  logger.log(QString("雷达数据时间间隔为%1ms").arg(radar_interval_));

  QLOG_INFO() << QString("有%1个雷达数据，开始测试...").arg(radar_data_.size());

  /*获得文件名中的标定信息*/
  mark_info_.clear();
  int ret = report->GetMarkInfo(file_name, mark_info_);
  if (mark_info_.size() == 0) {
    QLOG_INFO() << QString("视频数据未标定！，跳过该文件");
    return -1;
  }
  logger.log(QString("共有标定事件%1个:").arg(mark_info_.size()));
  for (const auto &mark_event : mark_info_) {
    logger.log(QString("[%1]: 雷达ID: %2, 车牌号: %3")
                   .arg(mark_event.type == 0 ? "入场" : "离场")
                   .arg(mark_event.id)
                   .arg(mark_event.plate_num));
  }

  /*清理上次算法处理结果, 清楚变量*/
  soc_on_ = false;
  soc_on_count_ = 0;
  alg_resl_.clear();
  return 0;
}

QString CThreadVideoTest::MakeLogFolder() {
  QString log_folder = folder_output_ + "/log";
  QDir dir;
  if (!dir.exists(log_folder)) {
    dir.mkpath(log_folder);
  }
  return log_folder;
}

QString CThreadVideoTest::GetMcuLogFileName(QString file_name) {
  QString name;
  QString path = MakeLogFolder();
  name = path + "/" + CGenOpt::GetFileNameFromPath(file_name) + "_mcu.log";
  return name;
}

QString CThreadVideoTest::GetTestLogFileName(QString file_name) {
  QString name;
  QString path = MakeLogFolder();
  name = path + "/" + CGenOpt::GetFileNameFromPath(file_name) + "_test.log";
  return name;
}

QString CThreadVideoTest::GetRadarChartFileName(QString file_name) {
  QString name;
  QString path = MakeLogFolder();
  name = path + "/" + CGenOpt::GetFileNameFromPath(file_name) + ".jpg";
  return name;
}

void CThreadVideoTest::run() {
  int ret = 0;
  CTestReport test_report;

  char log_file[256] = {0};
  /*校验测试文件列表是否为空*/
  if (file_list_.size() == 0) {
    QLOG_ERROR() << "文件夹内没有可以测试的文件";
    QLOG_ERROR() << "测试结束";
    return;
  }

  QLOG_INFO() << QString("共%1个文件，开始测试...").arg(file_list_.size());

  for (int i = 0; i < file_list_.size(); i++) {
    QString file_name = file_list_.at(i);
    QLogger logger(GetTestLogFileName(file_name));
    CGenOpt::QStringToChar(GetMcuLogFileName(file_name), log_file, 256);

    ret = RadarDet_Init(log_file, LOG_OUTPOS_FILE);
    if (ret != 0) {
      QLOG_ERROR() << "雷达检测初始化失败";
      QLOG_ERROR() << "测试结束";
      return;
    }
    RadarDet_SetSOCTrigCallBack(OnSocTrig, this);
    RadarDet_SetRadarTrigCallBack(OnRadarTrig, this);

    /*获得标记信息*/
    QLOG_INFO() << QString("开始测试文件(进度：%1%):%2")
                       .arg(i * 100 / file_list_.size())
                       .arg(file_name);
    if (DoBeforeTest(file_name, &test_report, logger) != 0) {
      continue;
    }
    do {
      OnAlgResultProcess(logger, file_name);
      RadarDet_Exec();
    } while (radar_index_ < (radar_data_.size() - 1));
    RadarDet_Uninit();
  }
  QLOG_ERROR() << "测试完成";
}
